package com.unknownman.block.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.lianziyou.chain.common.api.CommonPage;
import com.lianziyou.chain.common.utils.ConvertContractValue;
import com.unknownman.block.constants.ChainContractConstants;
import com.unknownman.block.entity.TContractConfig;
import com.unknownman.block.entity.TContractLogs;
import com.unknownman.block.entity.TTokenErc20;
import com.unknownman.block.mapper.TTokenErc20Mapper;
import com.unknownman.block.mapper.TTokenTransRecord;
import com.unknownman.block.service.TTokenErc20Service;
import com.unknownman.block.service.TTokenTransRecordService;
import com.unknownman.block.vo.ERC20HourHoldVO;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
import java.util.Map;
import java.util.Set;
import lombok.extern.slf4j.Slf4j;
import org.cloud.utils.DataDimensionUtil;
import org.cloud.vo.JavaBeanResultMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.web3j.abi.datatypes.Address;
import org.web3j.utils.Numeric;

/**
 * <p>
 * ERC20代币持仓信息 服务实现类
 * </p >
 *
 * @author xhy
 * @since 2021-10-18
 */
@Slf4j
@Service
public class TTokenErc20ServiceImpl extends ServiceImpl<TTokenErc20Mapper, TTokenErc20> implements TTokenErc20Service {

    @Autowired
    private TTokenTransRecordService tokenTransRecordService;

    @Value("${system.DataDimension.open:false}")
    private Boolean isSystemDataDimensionOpen;

    @Override
    public CommonPage<TTokenErc20> findListByPage(Integer page, Integer limit, String orderBy, QueryWrapper<TTokenErc20> queryWrapper) {

        if (isSystemDataDimensionOpen) {
            Set<String> contractAddressDataDimension = DataDimensionUtil.single().getCurrentUserDataDimensionByName("contract_address");
            if (!contractAddressDataDimension.contains("***")) {
                queryWrapper.in("token_address", contractAddressDataDimension);
            }
        }

        if (StringUtils.isEmpty(orderBy)) {
            PageHelper.startPage(page, limit);
        } else {
            PageHelper.startPage(page, limit, orderBy);
        }
        List<TTokenErc20> list = baseMapper.selectList(queryWrapper);
        return CommonPage.restPage(list);
    }

    @Override
    public TTokenErc20 getTokenByOwner(Long contractConfigId, String owner) {
        QueryWrapper<TTokenErc20> queryWrapper = new QueryWrapper<>();
        if (contractConfigId != null) {
            queryWrapper.eq("contract_config_id", contractConfigId);
        }
        if (!StringUtils.isEmpty(owner)) {
            queryWrapper.eq("owner_address", owner);
        }
        return getBaseMapper().selectOne(queryWrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveByLog(TContractLogs logs, TContractConfig contractConfig) {
        if (!logs.getFirstTopic().equalsIgnoreCase(ChainContractConstants.TRANSFER_EVENT_NAME)) {
            return false;
        }

        String from = logs.getSecondTopic();
        String to = logs.getThirdTopic();
        String logData = logs.getLogData();
        if (StringUtils.isEmpty(logs.getLogData()) || StringUtils.isEmpty(to) || StringUtils.isEmpty(logData)) {
            return false;
        }
        from = ConvertContractValue.convertToAddress(from);
        to = ConvertContractValue.convertToAddress(to);
        BigInteger bigValue = Numeric.decodeQuantity(logData);
        BigDecimal value = new BigDecimal(bigValue).divide(BigDecimal.TEN.pow(contractConfig.getErc20Decimals()));

        if (value.equals(BigDecimal.ZERO) || from.equals(to)) {
            return true;
        }
        TTokenTransRecord tTokenTransRecord = new TTokenTransRecord();
        tTokenTransRecord.setContractConfigId(logs.getContractConfigId());
        tTokenTransRecord.setTokenAddress(logs.getContractAddress());
        tTokenTransRecord.setBlockNumber(logs.getIndexForBlock());
        tTokenTransRecord.setTransactionHash(logs.getTransHash());
        tTokenTransRecord.setBlockTimestamp(logs.getBlockTimestamp());
        tTokenTransRecord.setCreateBy("system");
        tTokenTransRecord.setFromAddress(new Address(logs.getSecondTopic()).toString());
        tTokenTransRecord.setToAddress(new Address(logs.getThirdTopic()).toString());
        tTokenTransRecord.setTransAmount(value);
        tokenTransRecordService.getBaseMapper().insert(tTokenTransRecord);

        //处理from
        if (!from.equals(ChainContractConstants.ZERO_ADDRESS)) {
            TTokenErc20 tokenByOwnerFrom = getTokenByOwner(contractConfig.getContractConfigId(), from);
            if (tokenByOwnerFrom == null) {
                log.warn("saveByLog：转账from不存在于系统持仓数据中，[transHash:{}, from:{},to:{},value:{}]", logs.getTransHash(), from, to, value);
                TTokenErc20 tokenErc20 = new TTokenErc20();
                tokenErc20.setTokenSymbol(contractConfig.getSymbol());
                tokenErc20.setContractConfigId(contractConfig.getContractConfigId());
                tokenErc20.setOwnerAddress(from);
                tokenErc20.setTokenValue(BigDecimal.ZERO.subtract(value));
                tokenErc20.setTokenAddress(logs.getContractAddress());
                boolean save = getBaseMapper().insert(tokenErc20) > 0;
                Assert.isTrue(save, "saveByLog：保存交易from持仓数据失败");
            } else {
                int i = getBaseMapper().updateTokenValue(contractConfig.getContractConfigId(), from, BigDecimal.ZERO.subtract(value),
                    tokenByOwnerFrom.getTokenValue());
                Assert.isTrue(i > 0, "saveByLog：更新交易from持仓数据失败");
            }
        }
        // 处理to
        TTokenErc20 tokenByOwnerTo = getTokenByOwner(contractConfig.getContractConfigId(), to);
        if (tokenByOwnerTo == null) { //创建
            TTokenErc20 tokenErc20 = new TTokenErc20();
            tokenErc20.setTokenSymbol(contractConfig.getSymbol());
            tokenErc20.setContractConfigId(contractConfig.getContractConfigId());
            tokenErc20.setOwnerAddress(to);
            tokenErc20.setTokenValue(value);
            tokenErc20.setTokenAddress(logs.getContractAddress());
            boolean save = getBaseMapper().insert(tokenErc20) > 0;
            Assert.isTrue(save, "saveByLog：保存交易to持仓数据失败");

        } else { //更新
            int i = getBaseMapper().updateTokenValue(contractConfig.getContractConfigId(), to, value, tokenByOwnerTo.getTokenValue());
            Assert.isTrue(i > 0, "saveByLog：更新交易to持仓数据失败");
        }
        return true;
    }

    @Autowired
    MongoTemplate mongoTemplate;

    @Override
    public void saveTokenErc20HourHold() {
        List<ERC20HourHoldVO> erc20HourHoldVOList = this.getBaseMapper().queryTokenErc20ByHour(null);
        mongoTemplate.insert(erc20HourHoldVOList, __ERC20_HOUR_HOLD_COLLECTION_NAME);
    }

    @Override
    public List<ERC20HourHoldVO> queryTokenErc20ByHour(String tokenAddress) {
        return this.getBaseMapper().queryTokenErc20ByHour(tokenAddress);
    }

    @Override
    public List<JavaBeanResultMap> countTokenTotalByOwner(Map<String, Object> params) {
        return this.getBaseMapper().countTokenTotalByOwner(params);
    }


}